<template>
	<movable-area class="u-swipe-action" :style="{ width: `${width}rpx`, height: `${height}rpx`, backgroundColor: bgColor }">
		<movable-view
			class="u-swipe-view"
			@change="change"
			@touchend="touchend"
			direction="horizontal"
			:disabled="disabled"
			:x="moveX"
			:style="{
				width: movableViewWidth ? movableViewWidth : '100%'
			}"
		>
			<view class="u-swipe-content" @tap.stop="contentClick"><slot /></view>
			<view v-if="showBtn" class="u-swipe-del" :style="[getBtnStyle(item.style)]" v-for="(item, index) in options" :key="index" @tap.stop="btnClick(index)">
				<view class="u-btn-text">{{ item.text }}</view>
			</view>
		</movable-view>
	</movable-area>
</template>

<script setup>
import { ref, reactive, toRefs, computed, watch, getCurrentInstance, onMounted, nextTick } from 'vue'
const { ctx } = getCurrentInstance()
const emit = defineEmits(['open', 'close', 'click', 'content-click'])

const props = defineProps({
	// index值，用于得知点击删除的是哪个按钮
	index: {
		type: [Number, String],
		default: 0
	},
	//movable-area宽度
	width: {
		type: [String, Number],
		default: 690
	},
	//movable-area高度
	height: {
		type: [String, Number],
		default: 100
	},
	// 滑动按钮的宽度，单位为rpx
	btnWidth: {
		type: [String, Number],
		default: 180
	},
	// 是否禁止某个action滑动
	disabled: {
		type: Boolean,
		default: false
	},
	// 打开或者关闭组件
	show: {
		type: Boolean,
		default: false
	},
	// 组件背景颜色
	bgColor: {
		type: String,
		default: '#ffffff'
	},
	// 是否使手机发生短促震动，目前只在iOS的微信小程序有效(2020-05-06)
	vibrateShort: {
		type: Boolean,
		default: false
	},
	// 按钮操作参数
	options: {
		type: Array,
		default() {
			return [
				{
					text: '删除',
					style: {
						backgroundColor: '#f56c6c'
					}
				}
			]
		}
	}
})

let scrollX = 0, // movable-view移动过程中产生的change事件中的x轴移动值
	status = false, // 滑动的状态，表示当前是展开还是关闭按钮的状态
	movableAreaWidth = 0

const state = reactive({
	moveX: 0, // movable-view元素在x轴上需要移动的目标移动距离，用于展开或收起滑动的按钮
	showBtn: false // 刚开始渲染视图时不显示右边的按钮，避免视图闪动
})

const allBtnWidth = computed(() => uni.upx2px(props.btnWidth) * props.options.length)

let movableViewWidth = ref(0)

watch(
	() => props.show,
	(nVal, oVal) => {
		if (nVal) {
			open()
		} else {
			close()
		}
	}
)

onMounted(() => {
	nextTick(() => {
		getActionRect()
	})
})

const getBtnStyle = style => {
	return {
		...style,
		width: props.btnWidth + 'rpx'
	}
}

const getActionRect = () => {
	const query = uni
		.createSelectorQuery()
		// #ifndef MP-ALIPAY
		.in(ctx)
	// #endif

	query
		.select('.u-swipe-action')
		.boundingClientRect()
		.exec(res => {
			console.log('boundingClientRect: ', res)
			if (res) {
				// 滑动区域
				movableViewWidth.value = `${res[0].width + allBtnWidth.value}px`
				// movableAreaWidth.value = res[0].width
				// 等视图更新完后，再显示右边的可滑动按钮，防止这些按钮会"闪一下"
				nextTick(() => {
					state.showBtn = true
				})
			}
		})
}

// 关闭按钮状态
const close = () => {
	status = false
	state.moveX = 0
}

// 打开按钮的状态
const open = () => {
	if (props.disabled) return
	status = true
	state.moveX = -allBtnWidth.value
}

const emitOpenEvent = () => {
	emit('open', props.index)
}

const emitCloseEvent = () => {
	emit('close', props.index)
}

// 点击按钮
const btnClick = index => {
	// props.index为点击的几个组件，index为点击某个组件的第几个按钮(options数组的索引)
	emit('click', index, props.index)
	nextTick(() => {
		close()
	})
}

// 点击内容触发事件
const contentClick = () => {
	// 点击内容时，如果当前为打开状态，收起组件
	if (status == true) {
		status = false
		state.moveX = 0
	}
	emit('content-click', props.index)
}

// movable-view元素移动事件
const change = e => {
	scrollX = e.detail.x
}

// 用户手指离开movable-view元素，停止触摸
const touchend = () => {
	state.moveX = scrollX
	// 停止触摸时候，判断当前是展开还是关闭状态
	// 关闭状态
	// 这一步很重要，需要先给state.moveX一个变化的随机值，否则因为前后设置的为同一个值
	// props单向数据流的原因，导致movable-view元素不会发生变化，切记，详见文档：
	// https://uniapp.dcloud.io/use?id=%e5%b8%b8%e8%a7%81%e9%97%ae%e9%a2%98
	nextTick(() => {
		if (status == false) {
			// 关闭状态左滑，产生的x轴位移为负值，也就是说滑动的距离大于按钮的四分之一宽度，自动展开按钮
			if (scrollX <= -allBtnWidth.value / 4) {
				state.moveX = -allBtnWidth.value // 按钮宽度的负值，即为展开状态movable-view元素左滑的距离
				status = true // 标志当前为展开状态
				emitOpenEvent()
				// 产生震动效果
				if (props.vibrateShort) uni.vibrateShort()
			} else {
				state.moveX = 0 // 如果距离没有按钮宽度的四分之一，自动收起
				status = false
				emitCloseEvent()
			}
		} else {
			// 如果在打开的状态下，右滑动的距离X轴偏移超过按钮的四分之一(负值反过来的四分之三)，自动收起按钮
			if (scrollX > (-allBtnWidth.value * 3) / 4) {
				state.moveX = 0
				status = false
				emitCloseEvent()
			} else {
				state.moveX = -allBtnWidth.value
				status = true
				emitOpenEvent()
			}
		}
	})
}

const { moveX, showBtn } = toRefs(state)
</script>

<style lang="scss" scoped>
.u-swipe-action {
	width: auto;
	height: initial;
	overflow: hidden;
	//movable-area 必须设置 width 和 height 属性，不设置默认为 10px
}

.u-swipe-view {
	display: flex;
	height: 100%;
	/* 这一句很关键，覆盖默认的绝对定位 */
	//movable-view 必须设置width和height属性，不设置默认为10px
	//movable-view 默认为绝对定位，top和left属性为0px
}

.u-swipe-content {
	flex: 1;
}

.u-swipe-del {
	display: flex;
	justify-content: center;
	align-items: center;
	font-size: 30rpx;
	color: #fff;
}

.u-btn-text {
}
</style>
